package com.gitee.loyo;

import com.gitee.loyo.utils.DateTimeFormat;
import com.gitee.loyo.utils.HttpUtils;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClientResponse;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

public class Logs {
    public final static Map<String, List<RequestLog>> logs = new ConcurrentHashMap();
    public final static AtomicLong counter = new AtomicLong();

    public static void addLog(String domain, RequestLog log){
        List<Logs.RequestLog> list = logs.computeIfAbsent(domain, s -> new ArrayList<>());
        synchronized (domain){
            list.add(log);
            while (list.size() > Global.LOG_SIZE){
                list.remove(0);
            }
        }
    }

    public static class RequestLog{
        public final long num = counter.incrementAndGet();
        public final InBound in = new InBound();
        public final OutBound out = new OutBound();
        public final LocalDateTime startTime = LocalDateTime.now();
        public LocalDateTime endTime;

        public String formatStartTime(){
            return startTime.format(DateTimeFormat.DATE_TIME);
        }

        public void end(){
            endTime = LocalDateTime.now();
        }

        public BigDecimal cost(){
            return new BigDecimal(Duration.between(startTime, endTime).toMillis()).divide(new BigDecimal(1000));
        }

        public long getNum() {
            return num;
        }

        public InBound getIn() {
            return in;
        }

        public OutBound getOut() {
            return out;
        }
    }

    public static class InBound{
        public HttpMethod method;
        public String uri;
        public MultiMap headers;
        public Buffer body;

        public void init(HttpServerRequest request){
            method = request.method();
            uri = request.uri();
            headers = request.headers();
        }

        public void body(Buffer data) {
            if(body == null){
                body = Buffer.buffer();
            }
            if(body.length() < Global.LOG_BODY_LENGTH){
                body.appendBuffer(data, 0, Math.min(Global.LOG_BODY_LENGTH - body.length(), data.length()));
            }
        }

        public HttpMethod getMethod() {
            return method;
        }

        public String getUri() {
            return uri;
        }

        public MultiMap getHeaders() {
            return headers;
        }

        public Buffer getBody() {
            return body;
        }

        public String getBodyString(){
            return body == null ? "" : body.toString(StandardCharsets.UTF_8).replace("<", "&lt;").replace(">", "&gt;");
        }
    }

    public static class OutBound{
        public int code;
        public String message;
        public MultiMap headers;
        public Buffer body;

        public void init(HttpClientResponse response){
            code = response.statusCode();
            message = response.statusMessage();
            headers = response.headers();
        }

        public void init(HttpServerResponse response){
            code = response.getStatusCode();
            message = response.getStatusMessage();
            headers = response.headers();
        }

        public void body(Buffer data) {
            if(body == null){
                body = Buffer.buffer();
            }
            if(body.length() < Global.LOG_BODY_LENGTH){
                body.appendBuffer(data, 0, Math.min(Global.LOG_BODY_LENGTH - body.length(), data.length()));
            }
        }

        public int getCode() {
            return code;
        }

        public String getMessage() {
            return message;
        }

        public MultiMap getHeaders() {
            return headers;
        }

        public Buffer getBody() {
            return body;
        }

        public String getBodyString(){
            if(body == null)
                return "";
            if(headers.contains("Content-Encoding") && headers.get("Content-Encoding").contains("gzip")){
                return HttpUtils.decodeGZip(body.getBytes()).replace("<", "&lt;").replace(">", "&gt;");
            }
            return body.toString(StandardCharsets.UTF_8).replace("<", "&lt;").replace(">", "&gt;");
        }
    }
}
